﻿using AuSys.Database.Context;
using AuSys.Models;
using AuSys.Models.Identities;
using AuSys.Repos.Identities.Abstractions;
using AuSys.Services.App.Mapping;
using AuSys.Services.App.UserInfo.Abstractions;
using AuSys.Utils.Cryptography;
using AuSys.Utils.DependencyInjection;
using AuSys.Utils.StringUtils;
using AutoMapper;
using AutoMapper.QueryableExtensions;
using FluentValidation;
using System.ComponentModel;
using RqEx = AuSys.Utils.Exceptions.RequestInvalidException;

namespace AuSys.Repos.Identities
{
    public class UserRepo(
        DataContext context,
        LazyInject<IUserInfoService> userInfoService,
        IUserIdProvider userIdProvider,
        UserDtoValidator userDtoValidator,
        IMapper mapper
        ) : RepoTree<User, UserCache>(context), IBasicUserRepo
    {
        public int MatchUser(string username, string password, bool bypass)
        {
            if (bypass)
            {
                return Existing
                    .Where(x => x.Name == username)
                    .Select(x => x.Id)
                    .FirstOrDefault();
            }
            var pwdEncrypted = UserPwdEncryption.Encrypt(password);
            return Existing
                .Where(x => x.Name == username && x.Password == pwdEncrypted)
                .Select(x => x.Id)
                .FirstOrDefault();
        }

        public List<UserDto> IndexUser()
        {
            var q = Existing;
            if (!userInfoService.Value.GetUserInfo().IsAdmin)
            {
                var ids = GetMeAndMyDescendantIds();
                q = q.Where(x => ids.Contains(x.Id));
            }
            return [..q
                .OrderByDescending(x => x.Type)
                .ThenBy(x => x.Name)
                .ProjectTo<UserDto>(mapper.ConfigurationProvider)];
        }

        public List<UserDto> GetChildren(int id)
        {
            IQueryable<User> source;
            var myInfo = userInfoService.Value.GetUserInfo();
            var myId = myInfo.Id;
            var isAdmin = myInfo.IsAdmin;
            if(id == 0)
            {
                if (isAdmin)
                    source = FilterChildrenOf(0);
                else
                    source = Existing.Where(x => x.Id == myId);
            }
            else
                source = FilterChildrenOf(id);
            return [..source
                .ProjectTo<UserDto>(mapper.ConfigurationProvider)
                .ToList()];
        }

        public void CreateUser(UserDto u, bool devInit = false)
        {
            if (u.Id != 0)
                throw new RqEx("数据异常");
            var info = userInfoService.Value.GetUserInfo();
            if (u.Type >= UserType.Member && info.NotAdmin && !devInit)
            {
                //除管理员外只能创建游客
                throw new RqEx("无权创建该类型");
            }
            var vres = userDtoValidator.Validate(u);
            if (vres.Errors.Count > 0)
                throw new RqEx(vres.Errors.First().ErrorMessage);
            User user = mapper.Map<User>(u);
            base.Add(user);
        }

        public void UpdateUser(UserDto u)
        {
            if (u.Id == 0)
                throw new RqEx();
            var info = userInfoService.Value.GetUserInfo();
            var vres = userDtoValidator.Validate(u);
            if (vres.Errors.Count > 0)
                throw new RqEx(vres.Errors.First().ErrorMessage);
            var user = base.Get(u.Id)
                ?? throw new RqEx("找不到指定用户");
            if (info.NotAdmin)
            {
                if (user.Type == UserType.Tourist)
                {
                    //除管理员之外的用户不能转出游客类型
                    throw new RqEx("无权修改该类型");
                }
                if (u.Type == UserType.Admin)
                {
                    //除管理员之外的用户不能设为管理类型
                    throw new RqEx("无权设为该类型");
                }
            }
            bool wantChangeParent = user.ParentId != u.ParentId;
            if (wantChangeParent && info.NotAdmin)
            {
                //除管理员之外的用户不能编辑ParentId
                throw new RqEx("无权编辑所属上级");
            }
            mapper.Map(u, user);
            base.Update(user, true);
        }

        public void DeleteUser(int id)
        {
            var user = base.Get(id)
                ?? throw new RqEx("找不到指定用户");
            base.FakeRemove(user, true);
        }

        public bool IsDescendantOrSelfOfMe(int id)
        {
            var myId = userIdProvider.GetUserId();
            if (myId == 0)
                return false;
            if(myId == id)
                return true;
            return HaveAncestorRelation(myId, id);
        }

        public List<int> GetMeAndMyDescendantIds()
        {
            var myId = userIdProvider.GetUserId();
            if (myId == 0)
                return [];
            var res = GetDescendantIdsOf(myId);
            res.Add(myId);
            return res;
        }

        public UserCache? GetUserCache(int id)
            => CacheSearchSingle(x => x.Id == id);
        public IBasicUserInfo? GetUserInfoBasic(int id)
            => GetUserCache(id);

        public static string GetValidRandomPassword()
        {
            return new Random().Next(100000, 999999).ToString();
        }

        protected override IQueryable<UserCache> DbProjectToCache(IQueryable<User> source)
        {
            return source.ProjectTo<UserCache>(mapper.ConfigurationProvider);
        }
    }
    public class UserCache : ITreeNode, IBasicUserInfo
    {
        public int Id { get; set; }
        public string? Name { get; set; }
        public int ParentId { get; set; }
        public UserType Type { get; set; }
    }
    [DisplayName("用户")]
    public class UserDto
    {
        public int Id { get; set; }
        [DisplayName("用户名")]
        public string? Name { get; set; }
        [DisplayName("密码")]
        public string? Password { get; set; }
        public UserType Type { get; set; }
        public int AvatarFileId { get; set; }
        public string? AvatarUrl { get; set; }
        [DisplayName("简介")]
        public string? Intro { get; set; }
        public int ParentId { get; set; }
        public string? LastActive { get; set; }
    }
    public class UserDtoValidator : AbstractValidator<UserDto>
    {
        public UserDtoValidator(DataContext dataContext)
        {
            RuleFor(x => x.Name)
                .NotEmpty()
                .MinimumLength(2)
                .MaximumLength(User.nameMaxLength);
            When(x => (x.Id == 0 || !string.IsNullOrEmpty(x.Password)), () =>
            {
                //仅在新建用户、要修改密码时检查
                RuleFor(x => x.Password)
                    .NotEmpty()
                    .MinimumLength(6)
                    .MaximumLength(24);
            });
            RuleFor(x => x.Intro)
                .MaximumLength(User.introMaxLength)
                .When(x => !string.IsNullOrEmpty(x.Intro));
            RuleFor(x => x)
                .Must(x => !dataContext.Users.Any(u
                    => u.Id != x.Id && u.Name == x.Name))
                .WithMessage($"用户名已被占用");
        }
    }
    public class UserProfile : Profile
    {
        public UserProfile(){
            CreateMap<User, UserDto>()
                .ForMember(dist => dist.Password, opt => opt.Ignore())
                .ForMember(dist => dist.LastActive, opt => opt
                .MapFrom(src => src.LastUpdate.ToString(FormatStrings.DateTimeMin)));
            CreateMap<UserDto, User>()
                .ForMember(
                    destinationMember: dist => dist.Password,
                    memberOptions: opt =>
                    {
                        opt.Condition(src => !string.IsNullOrWhiteSpace(src.Password));
                        opt.AddTransform(meb => UserPwdEncryption.Encrypt(meb));
                    })
                .IgnoreLastUpdate();
            CreateMap<User, UserCache>();
        }
    }
}
